package com.ndp.fb.walle.util;

import com.ndp.fb.util.FileUtil;
import com.ndp.fb.walle.model.VideoMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by jacky.cheng on 2016/1/19.
 */
public class VideoUtils {
    private static Logger logger = LoggerFactory.getLogger(VideoUtils.class);
    private static String FFMPEG_PATH = "ffmpeg"; //env
    //    private static String FFMPEG_PATH = "D:\\ffmpeg-20160116-git-d7c75a5-win64-static\\bin\\ffmpeg.exe"; //env
    private static String FILE_SEPARATOR = File.separator;
    private static String SNAP_HOME = "/temp"; // TODO: 2016/1/19  to configuration

    public static void main(String[] args) {
        String home = "D:\\ffmpeg-20160116-git-d7c75a5-win64-static\\bin\\";
        File src = new File(home + "foo.mp4");
        VideoMeta record = VideoUtils.captureFirstFrame(src, home, "jpg");
        System.out.println(record.getName());
    }

    /**
     * 获取视频元数据
     *
     * @return
     * @throws FileNotFoundException
     */
    public static VideoMeta capture(File src) throws FileNotFoundException {
        if (src.exists()) {
            return captureFirstFrame(src, SNAP_HOME, "jpg");
        } else {
            throw new FileNotFoundException();
        }
    }

    /**
     * 获取视频元数据
     *
     * @param path 视频源路径
     * @return
     * @throws FileNotFoundException
     */
    public static VideoMeta capture(String path) throws FileNotFoundException {
        File file = new File(path);
        if (file.exists()) {
            return capture(file);
        } else {
            throw new FileNotFoundException();
        }
    }

    /**
     * 获取视频元数据
     *
     * @param src        视频源文件
     * @param destPath   缩略图路径
     * @param destSuffix 缩略图格式 default jpg
     * @return
     */
    public static VideoMeta captureFirstFrame(File src, String destPath, String destSuffix) {
        String srcName = src.getName();
        String srcSuffix = srcName.substring(srcName.lastIndexOf(".") + 1);
        if (!isSupportedType(srcSuffix)) throw new RuntimeException("unsupported file type " + srcSuffix);
        //目标文件名称
        String destName = destPath + FILE_SEPARATOR + srcName.substring(0, srcName.lastIndexOf("."));
        List<String> cmd = new LinkedList<>();
        cmd.add(FFMPEG_PATH);
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(src.getAbsolutePath());
        cmd.add("-vframes");
        cmd.add("1");
        cmd.add("-r");
        cmd.add("1");
        cmd.add("-ac");
        cmd.add("1");
        cmd.add("-ab");
        cmd.add("2");
        cmd.add("-s");
        cmd.add("116*116");
        cmd.add("-f");
        cmd.add("image2");
        cmd.add(destName + "." + destSuffix);
        VideoMeta videoMeta = execCmd(cmd);
        videoMeta.setName(src.getName()); //set name
        return videoMeta;
    }

    private static VideoMeta execCmd(List<String> cmd) {
        VideoMeta out = null;
        final ProcessBuilder pb = new ProcessBuilder();
        pb.redirectErrorStream(true);
        pb.command(cmd);
        try {
            final Process p = pb.start();
            InputStream in = p.getInputStream();
            out = parseMetaData(in);
            // 开启单独的线程来处理输入和输出流，避免缓冲区满导致线程阻塞.
            try {
                p.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            }
            p.getErrorStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return out;
    }

    /**
     * Input #0, avi, from 'c:\join.avi': Duration: 00:00:10.68(时长), start:
     * 0.000000(开始时间), bitrate: 166 kb/s(码率) Stream #0:0: Video: msrle
     * ([1][0][0][0] / 0x0001)(编码格式), pal8(视频格式), 165x97(分辨率), 33.33 tbr, 33.33
     * tbn, 33.33 tbc Metadata: title : AVI6700.tmp.avi Video #1
     */
    private static VideoMeta parseMetaData(InputStream is) throws IOException {
        String info = FileUtil.getInputContent(is);
        VideoMeta out = new VideoMeta();
        //解析时长和码率
        String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
        Pattern patDur = Pattern.compile(regexDuration);
        Matcher dm = patDur.matcher(info);
        if (dm.find()) {
            out.setDuration(Long.valueOf(getDuration(dm.group(1)) + ""));
//            out.setBeginTime(dm.group(2));
            out.setKb(dm.group(3) + "kb/s");
        }
        //解析分辨率
        String regSize = "[0-9]{3,4}x[0-9]{3,4}";
        Pattern patSize = Pattern.compile(regSize);
        Matcher sm = patSize.matcher(info);
        if (sm.find()) {
            String size = sm.group();
            String[] split = size.split("x");
            out.setWidth(Integer.valueOf(split[0]));
            out.setHeight(Integer.valueOf(split[1]));
        }
        //解析输出图片
        String regOutImg = "image2, to '(.*?)'";
        Pattern patImg = Pattern.compile(regOutImg);
        Matcher om = patImg.matcher(info);
        if (om.find()) {
            String path = om.group(1);
            logger.debug("img path," + path);
            File img = new File(path);
            if (img.exists()) {
                out.setImg(img);
            } else {
                logger.error("file not found!");
            }
        }
        return out;
    }

    /**
     * 时间转成毫秒数
     *
     * @param time 格式:"00:00:10.68"
     * @return 毫秒
     */
    private static int getDuration(String time) {
        int min = 0;
        String split[] = time.split(":");
        if (split[0].compareTo("0") > 0) {
            min += Integer.valueOf(split[0]) * 60 * 60 * 1000;//秒
        }
        if (split[1].compareTo("0") > 0) {
            min += Integer.valueOf(split[1]) * 60 * 1000;
        }
        if (split[2].compareTo("0") > 0) {
            min += Float.valueOf(split[2]) * 1000;
        }
        return min;
    }

    private static boolean isSupportedType(String type) {
        Pattern pattern = Pattern.compile("(asx|asf|mpg|wmv|3gp|mp4|mov|avi|flv){1}$", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(type);
        return matcher.find();
    }
}
